package com.ansion.basedb.dao.helper;

import android.text.TextUtils;

import androidx.annotation.NonNull;

import com.ansion.basedb.dao.UserFTSHelper;
import com.ansion.basedb.dao.entity.FtsUserEntity;
import com.ansion.basedb.dao.entity.GroupUserEntity;
import com.ansion.basedb.dao.model.ContactsModel;
import com.ansion.basedb.dao.model.GroupModel;
import com.ansion.basedb.dao.model.UserModel;
import com.ansion.basedb.database.DatabaseTools;
import com.ansion.basedb.database.SqlQueryUtil;
import com.ansion.basedb.database.sql.SQLColumn;
import com.ansion.basedb.database.sql.SQLJoinType;
import com.ansion.basedb.storage.StorageSdk;
import com.ansion.basedb.storage.api.BaseKeyValueStorage;
import com.ansion.constant.BConstant;
import com.ansion.log.VLog;
import com.ansion.utils.BooleanCallback;
import com.ansion.utils.ContactUtil;
import com.ansion.utils.ThreadUtil;
import com.ansion.utils.ValueSafeTransfer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;


/**
 * created by zhaoyuntao
 * on 07/12/2023
 * description: 目前的产品需求里，群是从内存查询的，所以群没有必要去建索引了，仅需创建群成员索引。
 * 1、UserModel按照updateTime作为分界线，首次升级/冷启动保存一下当前时间作为用户索引同步分界时间线，
 * 只查出来updateTime小于这个时间的记录，并与ContactsModel表left join。由于UserModel也会存OA，
 * 所以查出来数据后要手动过滤掉OA，只保留纯正的UserModel。当联系人名变更的时候判断一下UserModel.updateTime
 * 如果比当前索引创建进度的updateTime小则不能更新，比它大则可以
 * 2、GroupModel添加sp存每个gid的GroupUser中间表是否创建完毕，如果没有创建完毕则群成员变更不能更新GroupUser表中
 * 的这个gid的记录，如果创建完毕则可以更新
 */
public class UserIndexHistoryHelper {
    //用户/群索引同步状态，未做完还是做完
    public static final String KEY_STORAGE_STATE = BConstant.MIGRATE.KEY_FTS_USER_STORAGE_STATE;
    /**
     * 某个群id是否创建过索引，用于在群成员变更的时候lastRequestTime<=lastGroupRequestTime的时候决定是否需要去更新GroupUser表
     */
    public static final String KEY_GROUP_INDEX_STATE = "UserFtsRecord-groupIndexState-";
    /**
     * 每次创建10条记录的索引，按照10条去做分页
     */
    public static final int COUNT_INDEX_ONETIME = 20;
    public static final int STATE_NOT_READY = BConstant.MIGRATE.INDEX_STATE_NOT_READY;
    public static final int STATE_COMPLETED = BConstant.MIGRATE.INDEX_STATE_COMPLETED;
    /**
     * 索引同步是否开始
     **/
    static final AtomicBoolean indexUserStart = new AtomicBoolean(false);
    static final AtomicBoolean indexGroupStart = new AtomicBoolean(false);

    public static void compatIndexHistoryUsersAndGroups() {
        _compatIndex(true);
        _compatIndex(false);
    }

    public static void debugClearFTSIndexHistory() {
        _debugClearFTSIndexHistoryUsersAndGroupMembers(true);
        _debugClearFTSIndexHistoryUsersAndGroupMembers(false);
        indexUserStart.set(false);
    }

    private static BaseKeyValueStorage getStorage() {
        return FtsIndexHistoryHelper.getStorage();
    }

    @NonNull
    private static String getStorageKey(boolean indexUser, @NonNull String key) {
        return BConstant.MIGRATE.getUserIndexStateKey(indexUser, key);
    }

    private static void _compatIndex(boolean indexUser) {

        if (indexUser) {
            if (!indexUserStart.compareAndSet(false, true)) {
                VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsers already started,return");
                return;
            }
        } else {
            if (!indexGroupStart.compareAndSet(false, true)) {
                VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldGroups already started,return");
                return;
            }
        }
        BaseKeyValueStorage storage = getStorage();
        int state = storage.getInt(getStorageKey(indexUser, KEY_STORAGE_STATE), STATE_NOT_READY);
        if (state == STATE_COMPLETED) {
            VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups[" + getTag(indexUser) + "] already completed");
            if (indexUser) {
                indexUserStart.set(false);
            } else {
                indexGroupStart.set(false);
            }
        } else if (state == STATE_NOT_READY) {
            indexOneSetOfUsers(storage, indexUser, 0);
        }
    }

    private static void indexOneSetOfUsers(@NonNull BaseKeyValueStorage storage, boolean indexUser, int lastIndexUserOffset) {
        VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups[" + getTag(indexUser) + "] run");
        long lastIndexUserUpdateTime = indexUser ? FtsIndexHistoryHelper.getFirstInstallSystemTime() : FtsIndexHistoryHelper.getFirstInstallServerTime();
        if (lastIndexUserUpdateTime <= 0) {
            if (indexUser) {
                indexUserStart.set(false);
            } else {
                indexGroupStart.set(false);
            }
//            AppFetalHelper.trackError(BConstant.FootPrint.Search.TRACK_EVENT_NAME, indexUser ? BConstant.FootPrint.Search.Key_Error_Reason_Index_User_Fail : BConstant.FootPrint.Search.Key_Error_Reason_Index_GroupMember_Fail, "FirstInstallTime is " + lastIndexUserUpdateTime);
            return;
        }

        if (indexUser) {
            String userTableName = DatabaseTools.getTableName(UserModel.class);
            String contactTaleName = DatabaseTools.getTableName(ContactsModel.class);
            String query = "SELECT " + SqlQueryUtil.buildSqlSelect(
                    SQLColumn.getColumnName(userTableName, UserModel.kColumnName_UserId),
                    SQLColumn.getColumnName(userTableName, UserModel.kColumnName_UpdateTime),
                    SQLColumn.getColumnName(userTableName, UserModel.kColumnName_NickName),
                    SQLColumn.getColumnName(contactTaleName, ContactsModel.kColumnName_FirstName),
                    SQLColumn.getColumnName(contactTaleName, ContactsModel.kColumnName_MiddleName),
                    SQLColumn.getColumnName(contactTaleName, ContactsModel.kColumnName_LastName),
                    SQLColumn.getColumnName(contactTaleName, ContactsModel.kColumnName_ContactId)
            )
                    + " FROM " + userTableName
                    + SqlQueryUtil.buildSqlJoin(SQLJoinType.TYPE_LEFT_JOIN, userTableName, UserModel.kColumnName_UserId,
                    contactTaleName, ContactsModel.kColumnName_UserId
            )
                    + " WHERE " + SqlQueryUtil.buildSqlLessOrEqual(UserModel.kColumnName_UpdateTime, lastIndexUserUpdateTime)
                    + SqlQueryUtil.buildSqlOrderBy(UserModel.kColumnName_UpdateTime, true)
                    + SqlQueryUtil.buildMoreSqlOrderBy(SqlQueryUtil.buildSqlQueryColumn(userTableName, UserModel.kColumnName_UserId), true)
                    + SqlQueryUtil.buildSqlLimit(COUNT_INDEX_ONETIME, lastIndexUserOffset);
            List<UserQueryBeanForUI> list = StorageSdk.getMainDB().select(UserQueryBeanForUI::new, query);
            if (list.isEmpty()) {
                VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups[" + getTag(indexUser) + "] query data is empty,no need to index.");
                onIndexCompleted(storage, indexUser, false, lastIndexUserOffset);
                return;
            }
            boolean hasMore = list.size() >= COUNT_INDEX_ONETIME;
            VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups[" + getTag(indexUser) + "][size:" + list.size() + "] start");
            List<FtsUserEntity> users = ValueSafeTransfer.transformList(list, (position, t) -> {
                if (ContactUtil.isUser(t.getUserId()) && !ContactUtil.isSystemId(t.getUserId())) {//只有用户才去创建索引
                    return new FtsUserEntity().set(t.getUserId(), t.getContactId() > 0, t.getNickName(), t.getFirstName(), t.getMiddleName(), t.getLastName());
                }
                return null;
            });
            Map<String, FtsUserEntity> userMap = new HashMap<>();
            ValueSafeTransfer.iterateAll(users, (p, u) -> {
                userMap.put(u.getUid(), u);
            });
            UserFTSHelper.compareAndIndexUser(userMap, new BooleanCallback() {
                @Override
                public void onResult(boolean result) {
                    if (!result) {
                        indexUserStart.set(false);
                        VLog.FileLog.e("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups fail %s,return", indexUser);
                        return;
                    }
                    int offset = lastIndexUserOffset + COUNT_INDEX_ONETIME;
                    VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups set %s lastUserOffset:%s", getTag(indexUser), offset);
                    onIndexCompleted(storage, indexUser, hasMore, offset);
                }

                @Override
                public void onFail(Throwable tr) {
                    indexUserStart.set(false);
                    String errorMsg = "";
                    if (tr != null) {
                        errorMsg = tr.getMessage() == null ? "" : tr.getMessage();
                    }
//                    AppFetalHelper.trackError(BConstant.FootPrint.Search.TRACK_EVENT_NAME, BConstant.FootPrint.Search.Key_Error_Reason_Index_User_Fail, errorMsg);

                }
            });
        } else {
            String groupTableName = DatabaseTools.getTableName(GroupModel.class);
            String query = "SELECT " + SqlQueryUtil.buildSqlSelect(GroupModel.kColumnName_Group_Id, GroupModel.kColumnName_Members
                    , GroupModel.kColumnName_lastRequestTime)
                    + " FROM " + groupTableName
                    + " WHERE " + SqlQueryUtil.buildSqlLessOrEqual(GroupModel.kColumnName_lastRequestTime, lastIndexUserUpdateTime)
                    + SqlQueryUtil.buildSqlOrderBy(GroupModel.kColumnName_lastRequestTime, true)
                    + SqlQueryUtil.buildMoreSqlOrderBy(GroupModel.kColumnName_Group_Id, true)
                    + SqlQueryUtil.buildSqlLimit(COUNT_INDEX_ONETIME, lastIndexUserOffset);


            List<GroupQueryBeanForUI> list = StorageSdk.getMainDB().select(GroupQueryBeanForUI::new, query);
            if (list.isEmpty()) {
                VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups[" + getTag(indexUser) + "] query data is empty,no need to index.");
                onIndexCompleted(storage, indexUser, false, 0);
                return;
            }
            boolean hasMore = list.size() >= COUNT_INDEX_ONETIME;
            VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups[" + getTag(indexUser) + "][size:" + list.size() + "] start ");
            List<GroupUserEntity> groupMembers = new ArrayList<>();
            ValueSafeTransfer.iterateAll(list, (position, groupQueryBeanForUI) -> {
                if (TextUtils.isEmpty(groupQueryBeanForUI.getGid())) {
                    return;
                }
                ValueSafeTransfer.iterateAll(groupQueryBeanForUI.getGroupMembers(), (p, memberUid) -> {
                    if (!TextUtils.isEmpty(memberUid))
                        groupMembers.add(new GroupUserEntity().setGid(groupQueryBeanForUI.getGid()).setMemberUid(memberUid));
                });
            });
            VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups[" + getTag(indexUser) + "][groupMembers:" + groupMembers.size() + "] start ");
            UserFTSHelper.indexGroupMemberOnly(groupMembers, new BooleanCallback() {
                        @Override
                        public void onResult(boolean result) {
                            if (!result) {
                                indexGroupStart.set(false);
                                VLog.FileLog.e("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups indexGroupMemberOnly fail,return");
                                return;
                            }
                            int offset = lastIndexUserOffset + COUNT_INDEX_ONETIME;
                            VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups set lastGroupOffset:%s", offset);
                            ValueSafeTransfer.iterateAll(list, (position, groupQueryBeanForUI) -> {
                                storage.putBoolean(KEY_GROUP_INDEX_STATE + groupQueryBeanForUI.getGid(), true);
                                VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups mark group index finish %s one set completed", groupQueryBeanForUI.getGid());
                            });
                            onIndexCompleted(storage, indexUser, hasMore, offset);
                        }

                        @Override
                        public void onFail(Throwable tr) {
                            indexGroupStart.set(false);
                            String errorMsg = "";
                            if (tr != null) {
                                errorMsg = tr.getMessage() == null ? "" : tr.getMessage();
                            }
//                            AppFetalHelper.trackError(BConstant.FootPrint.Search.TRACK_EVENT_NAME, BConstant.FootPrint.Search.Key_Error_Reason_Index_GroupMember_Fail, errorMsg);
                        }
                    }


            );
        }
    }

    private static void onIndexCompleted(@NonNull BaseKeyValueStorage storage, boolean indexUser, boolean hasMore, int lastOffset) {
        VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups[%s] one set completed,hasMore:%b", getTag(indexUser), hasMore);
        if (hasMore) {
            ThreadUtil.runSingleTask(() -> {
                indexOneSetOfUsers(storage, indexUser, lastOffset);
            });
        } else {
            if (indexUser) {
                indexUserStart.set(false);
            } else {
                indexGroupStart.set(false);
            }
            storage.putInt(getStorageKey(indexUser, KEY_STORAGE_STATE), STATE_COMPLETED);
            VLog.FileLog.i("TAG_SEARCH_FTS", "FTS compatIndexOldUsersAndGroups[%s] finish", getTag(indexUser));
        }
    }

    private static String getTag(boolean indexUser) {
        return indexUser ? BConstant.MIGRATE.TAG_USER : BConstant.MIGRATE.TAG_GROUP;
    }

    private static void _debugClearFTSIndexHistoryUsersAndGroupMembers(boolean indexUser) {
        BaseKeyValueStorage storage = getStorage();
        storage.remove(getStorageKey(indexUser, KEY_STORAGE_STATE));
    }


    private static boolean indexComplete(boolean indexUser) {
        return getStorage().getInt(getStorageKey(indexUser, KEY_STORAGE_STATE), STATE_NOT_READY) == STATE_COMPLETED;
    }

    public static boolean indexComplete() {
        return indexComplete(true) && indexComplete(false);
    }


    public static boolean userIndexComplete() {
        int state = getStorage().getInt(getStorageKey(true, KEY_STORAGE_STATE), STATE_NOT_READY);
        return state == STATE_COMPLETED;
    }

    public static boolean groupIndexComplete() {
        int state = getStorage().getInt(getStorageKey(false, KEY_STORAGE_STATE), STATE_NOT_READY);
        return state == STATE_COMPLETED;
    }

    public static boolean groupIndexFinish(String gid) {
        return getStorage().getBoolean(KEY_GROUP_INDEX_STATE + gid, false);
    }


}
